package com.demo.sortcsv;

import com.demo.sortcsv.domain.CanDetailDongFeng;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.csv.CSVRecord;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.*;
import java.lang.reflect.Field;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * csv文件读写工具类
 *
 * @author by YangLD
 * @date 2018/7/10
 */
@Slf4j
public class CsvUtils {
    private static final DecimalFormat decimalFormat = new DecimalFormat("##0.00");
    private static final String ROW_SPLIT = "\r\n";
    private final static SimpleDateFormat dateFormat = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
    /**
     * 写csv文件 (一次性写  数据不宜过大)
     *
     * @param objectList  对象
     * @param fileHeader  头说明
     * @param fileName    文件名称(不要后缀.csv)
     * @return File 文件
     */
    public static File writeCsv(List<Object> objectList, String[] fileHeader, String fileName) {
        // 这里显式地配置一下CSV文件的Header，然后设置跳过Header（要不然读的时候会把头也当成一条记录）
        CSVFormat format = CSVFormat.DEFAULT.withHeader(fileHeader).withRecordSeparator("\n");
        // 这个是定位   判断某个字段的数据应该放在records数组中的那个位子
        Map<String, Integer> map = Maps.newHashMap();
        for (int i = 0; i < fileHeader.length; i++) {
            map.put(fileHeader[i], i);
        }
        File csvFile = new File(fileName);
        try {
            // 获取对象的PropertyDescriptor
            Map<String, PropertyDescriptor> descriptorMap = null;
            // 附加
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(csvFile), "UTF-8"));
            CSVPrinter printer = new CSVPrinter(bw, format);
            for (Object object : objectList) {
                if(CheckUtils.isEmpty(descriptorMap)){
                    descriptorMap = CsvUtils.getCsvFieldMapPropertyDescriptor(object.getClass());
                }
                String[] records = new String[fileHeader.length];
                for (Map.Entry<String, Integer> stringIntegerEntry : map.entrySet()) {
                    if (descriptorMap.containsKey(stringIntegerEntry.getKey())) {
                        records[map.get(stringIntegerEntry.getKey())] = (String) descriptorMap.get(stringIntegerEntry.getKey()).getReadMethod().invoke(object);
                    }
                }
                printer.printRecord(Arrays.asList(records));
            }
            bw.flush();
            bw.close();
            printer.close();
        } catch (Exception e) {
            log.error("CsvUtils.writeCsv,写csv文件失败,message:{}", e.getMessage(), e);
        }
        return csvFile;
    }

    /**
     * 获取对应对象中包含CsvCsvField字段的 PropertyDescriptor
     *
     * @param tClass 对象的class
     * @return Map
     * @throws Exception 异常
     */
    public static Map<String, PropertyDescriptor> getCsvFieldMapPropertyDescriptor(Class tClass) throws Exception {
        Map<String, PropertyDescriptor> descriptorMap = Maps.newHashMap();
        BeanInfo beanInfo = Introspector.getBeanInfo(tClass);
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            // 获取该字段赋值过来的  字段名称
            if (propertyDescriptor.getWriteMethod() == null) {
                continue;
            }
            Field field = tClass.getDeclaredField(propertyDescriptor.getName());
            CsvField csvField = field.getAnnotation(CsvField.class);
            if (csvField == null) {
                continue;
            }
            String fieldMetaName = csvField.name();
            if (CheckUtils.isEmpty(fieldMetaName)) {
                continue;
            }
            descriptorMap.put(fieldMetaName, propertyDescriptor);
        }
        return descriptorMap;
    }

    /**
     * 读取csv文件  (一次性读取文件不宜过大)
     *
     * @param filePath 文件路径
     * @param headers  csv列头
     * @param tClass   返回对象的类型
     * @return CSVRecord 列表
     **/
    public static <T> List<T> readCSV(String filePath, String[] headers, Class<T> tClass) {
        //创建CSVFormat
        CSVFormat format = CSVFormat.DEFAULT.withHeader(headers);
        // 获取对象的PropertyDescriptor
        List<T> tList = new ArrayList<>();
        try {
            Map<String, PropertyDescriptor> descriptorMap = CsvUtils.getCsvFieldMapPropertyDescriptor(tClass);
            FileReader fileReader = new FileReader(filePath);
            //创建CSVParser对象
            CSVParser parser = new CSVParser(fileReader, format);
            Map<String, Integer> map = parser.getHeaderMap();
            for (CSVRecord record : parser) {
                T t = tClass.newInstance();
                for (Map.Entry<String, Integer> stringIntegerEntry : map.entrySet()) {
                    if (descriptorMap.containsKey(stringIntegerEntry.getKey()) && record.size() > stringIntegerEntry.getValue()) {
                        descriptorMap.get(stringIntegerEntry.getKey()).getWriteMethod().invoke(t, record.get(stringIntegerEntry.getValue()));
                    }
                }
                tList.add(t);
            }
            parser.close();
            fileReader.close();
        } catch (Exception e) {
            log.error("CsvUtils.readCSV,读取csv文件,message:{}", e.getMessage(), e);
        }
        return tList;
    }

    public static List<CanDetailDongFeng> readCsv(String path) throws IOException {
        List<CanDetailDongFeng> dataList = new ArrayList<>();
        FileReader fileReader = new FileReader(new File(path));
        BufferedReader bufferedReader = new BufferedReader(fileReader);
        // 去除第一行
        bufferedReader.readLine();
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            CanDetailDongFeng canDetailDongFeng = new CanDetailDongFeng();
            String[] split = line.split("，");
            String last = split[split.length - 1].replaceAll("\"","");
            String[] data = last.split(",");
            try {
                Date upLoadTime = dateFormat.parse(data[0]);
                Date createTime = dateFormat.parse(data[1]);
                canDetailDongFeng.setUploadTime(upLoadTime);
                canDetailDongFeng.setCreateTime(createTime);
                canDetailDongFeng.setEngineSpeed(Integer.parseInt(data[2]));
                canDetailDongFeng.setEngineFuel(Double.parseDouble(data[3]));
                canDetailDongFeng.setEngineTotalFuel(Double.parseDouble(data[4]));
                canDetailDongFeng.setBrake(Integer.parseInt(data[5]));
                canDetailDongFeng.setVehicleSpeedFefl(Double.parseDouble(data[6]));
                canDetailDongFeng.setVehicleSpeedFe6c(Double.parseDouble(data[7]));
                canDetailDongFeng.setTotalDistanceFec1(Double.parseDouble(data[8]));
                canDetailDongFeng.setTotalDistanceFee0(Double.parseDouble(data[9]));
                canDetailDongFeng.setThrottleValve(Integer.parseInt(data[10]));
                canDetailDongFeng.setEngineCoolantTemperature(Double.parseDouble(data[11]));
                canDetailDongFeng.setFuelPressure(Integer.parseInt(data[12]));
                canDetailDongFeng.setManifoldAbsolutePressure(Double.parseDouble(data[13]));
                canDetailDongFeng.setEngineInletTemperature(Double.parseDouble(data[14]));
                canDetailDongFeng.setAirFlow(Double.parseDouble(data[15]));
                canDetailDongFeng.setSolarTermDoorPositionSensor(Double.parseDouble(data[16]));
                canDetailDongFeng.setEngineRunningTime(Double.parseDouble(data[17]));
                canDetailDongFeng.setStrokeFuel(Integer.parseInt(data[18]));
                canDetailDongFeng.setTorque(Integer.parseInt(data[19]));

                dataList.add(canDetailDongFeng);
            } catch (ParseException e) {
                log.error("time formatted error "+data[0]+" is invalid");
            }
        }

        return dataList;
    }

    public static void writeCsv(List<CanDetailDongFeng> data,String outPath) throws IOException {
        CSVColumn csvColumn = new CSVColumn();
        File file = new File(outPath);
        boolean exsits = file.exists();
        try (FileOutputStream outputStream = new FileOutputStream(file,true);
        OutputStreamWriter writer = new OutputStreamWriter(outputStream, "GBK")){
            StringBuilder sb = new StringBuilder();
            if (!exsits) {
                // insert column head in csv file
                int count = 0;
                for (String schema : csvColumn.getColumns()) {
                    count++;
                    if (count < csvColumn.getColumns().size()) {
                        wrapStr(sb, schema);
                    } else {
                        sb.append("\"").append(schema).append("\"");
                    }
                }
                sb.append(ROW_SPLIT);
            }
            for (CanDetailDongFeng canDetailDongFeng : data) {
                wrapStr(sb, canDetailDongFeng.getUploadTime());
                wrapStr(sb, canDetailDongFeng.getCreateTime());
                wrapStr(sb, canDetailDongFeng.getEngineSpeed());
                wrapStr(sb, canDetailDongFeng.getEngineFuel());
                wrapStr(sb, canDetailDongFeng.getEngineTotalFuel());
                wrapStr(sb, canDetailDongFeng.getBrake());
                wrapStr(sb, canDetailDongFeng.getVehicleSpeedFefl());
                wrapStr(sb, canDetailDongFeng.getVehicleSpeedFe6c());
                wrapStr(sb, canDetailDongFeng.getTotalDistanceFec1());
                wrapStr(sb, canDetailDongFeng.getTotalDistanceFee0());
                wrapStr(sb, canDetailDongFeng.getThrottleValve());
                wrapStr(sb, canDetailDongFeng.getEngineCoolantTemperature());
                wrapStr(sb, canDetailDongFeng.getFuelPressure());
                wrapStr(sb, canDetailDongFeng.getManifoldAbsolutePressure());
                wrapStr(sb, canDetailDongFeng.getEngineInletTemperature());
                wrapStr(sb, canDetailDongFeng.getAirFlow());
                wrapStr(sb, canDetailDongFeng.getSolarTermDoorPositionSensor());
                wrapStr(sb, canDetailDongFeng.getEngineRunningTime());
                wrapStr(sb, canDetailDongFeng.getStrokeFuel());
                sb.append("\"").append(canDetailDongFeng.getTorque()).append("\"").append(ROW_SPLIT);
            }
            writer.write(sb.toString());
        }
    }
    private static void wrapStr(StringBuilder sb, Object value) {
        // 如果是double类型，避免用科学计数法显示
        if (value instanceof Double) {
            sb.append("\"").append(decimalFormat.format((double) value)).append("\"")
                    .append(",");
        } else {
            sb.append("\"").append(value).append("\"").append(",");
        }
    }
}